package com.sunyzc.ebaotech.simulator.ybt.abc;

import java.util.Arrays;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

import com.sunyzc.ebaotech.simulator.HashUtil;
import com.sunyzc.ebaotech.simulator.ybt.AbstractYBTProcessor;

/**
 * 1000 - 心跳交易(998) <br>
 * 1001 - 密钥重置() <br>
 * 1002 - 新单试算(104) <br>
 * 1003 - 新单核保结果通知(暂不启用) <br>
 * 1004 - 新单缴费(102) <br>
 * 1005 - 新单试算结果查询(110) <br>
 * 1006 - 非实时出单申请(114) <br>
 * 1007 - 续期缴费查询(301) <br>
 * 1008 - 续期缴费(303) <br>
 * 1009 - 取消交易(202) <br>
 * 1010 - 当日撤单(201) <br>
 * 1011 - 账户变更(115) <br>
 * 1012 - 保全查询(117) <br>
 * 1013 - 保全申请(116) <br>
 * 1014 - 保全申请状态查询(118) <br>
 * 1015 - 账户设置(公司发起) <br>
 * 1016 - 保单查询(106) <br>
 * 1017 - 文件上传下载 <br>
 * 1018 - 保单重打(103) <br>
 * 1019 - 保单价值查询(107) <br>
 * 1020 - 非实时出单申请查询(暂不启用) <br>
 * 1021 - 保单详情查询(121) <br>
 * 1022 - 保费估算(205) <br>
 * 801 - 日结对账(801)(注意实际是没有对账请求的交易的) <br>
 */
public class ABCProcessor extends AbstractYBTProcessor {
	private final static int HEAD_LENGTH = 73;
	private final static int BODY_LENGTH_BEGIN_INDEX = 4;
	private final static int BODY_LENGTH_END_INDEX = 12;
	/**
	 * AES/CBC/NoPadding 模式要求： <br>
	 * 密钥必须是16位 <br>
	 * Initialization vector (IV) 必须是16位 <br>
	 * 待加密内容的字节长度必须是16的倍数，如果不是16的倍数，就会出现异常 javax.crypto.IllegalBlockSizeException: Input length not multiple of 16 bytes<br>
	 */
	private final static String CIPHER_ALGORITHM = "AES/CBC/Nopadding";
	private final static SecretKey SECRET_KEY = new SecretKeySpec("5e4757ec8b8da551".getBytes(), "AES");
	private final static IvParameterSpec IV = new IvParameterSpec("ABCHINA..ANIHCBA".getBytes()); // IV length must be 16 bytes long

	/**
	 * 报文类型 C1 固定为“X”，表示标准接口 <br>
	 * 版本号 C3 当前版本为1.0 <br>
	 * 数据包长度 N8 包体的长度，包体的长度不能超过50K字节。如果加密，指加密后的长度；如果压缩，指定压缩后的长度 <br>
	 * 公司代码 C8 银行分配给公司的代码 <br>
	 * 加密标示 C1 数据包是否加密：0-不加密；1-关键数据加密；2-报文整体加密 <br>
	 * 加密算法 C1 <br>
	 * 数据压缩标志 C1 数据包是否压缩传输：0-不压缩；1-压缩 <br>
	 * 数据压缩算法 C1 <br>
	 * 摘要算法 C1 <br>
	 * 摘要 C40 <br>
	 * 预留字段 C8 扩展域，固定值00000000 <br>
	 */
	@Override
	public byte[] generateHeadBytes(String requestTransType, byte[] bodyContent) throws Exception {
		String requestHeadContent = "X1.0" + String.format("%08d", bodyContent == null ? 0 : bodyContent.length) + "3112    " + "2 0  " + HashUtil.getSHA1(bodyContent) + "00000000";
		return requestHeadContent.getBytes(getClientConfig().getTransEncoding());
	}

	@Override
	protected byte[] beforeSend(String bodyContentStr) throws Exception {
		return getClientConfig().isNeedCipher() ? parseBytes2HexStr(encrypt(bodyContentStr.getBytes(getClientConfig().getTransEncoding()))).getBytes(getClientConfig().getTransEncoding()) : super.beforeSend(bodyContentStr);
	}

	@Override
	protected byte[] afterReceived(byte[] bodyContentBytes) throws Exception {
		return getClientConfig().isNeedCipher() ? decrypt(parseHexStr2Bytes(new String(bodyContentBytes, getClientConfig().getTransEncoding()))) : super.afterReceived(bodyContentBytes);
	}

	@Override
	protected int getHeadLength() {
		return HEAD_LENGTH;
	}

	@Override
	protected int getBodyLengthBeginIndex() {
		return BODY_LENGTH_BEGIN_INDEX;
	}

	@Override
	protected int getBodyLengthEndIndex() {
		return BODY_LENGTH_END_INDEX;
	}

	private static byte[] encrypt(byte[] originalContent) throws Exception {
		originalContent = Arrays.copyOf(originalContent, originalContent.length % 16 == 0 ? originalContent.length : (originalContent.length >> 4) + 1 << 4);
		return encryptMsg(SECRET_KEY, originalContent);
	}

	private static byte[] decrypt(byte[] encryptedContent) throws Exception {
		return decryptMsg(SECRET_KEY, encryptedContent);
	}

	private static byte[] encryptMsg(SecretKey aesKey, byte[] msgBytes) throws Exception {
		Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
		cipher.init(Cipher.ENCRYPT_MODE, aesKey, IV);
		return cipher.doFinal(msgBytes);
	}

	private static byte[] decryptMsg(SecretKey aesKey, byte[] msgBytes) throws Exception {
		Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
		cipher.init(Cipher.DECRYPT_MODE, aesKey, IV);
		return cipher.doFinal(msgBytes);
	}

	private static String parseBytes2HexStr(byte[] bytes) {
		StringBuffer hexStringBuffer = new StringBuffer(bytes.length << 1);
		for (byte b : bytes) {
			String hexStr = Integer.toHexString(b & 0xFF);
			if (hexStr.length() == 1)
				hexStr = '0' + hexStr;
			hexStringBuffer.append(hexStr.toUpperCase());
		}
		return hexStringBuffer.toString();
	}

	private static byte[] parseHexStr2Bytes(String hexStr) {
		if (hexStr == null || hexStr.length() % 2 == 1)
			return null;
		byte[] bytesResult = new byte[hexStr.length() >> 1];
		for (int index = 0; index != bytesResult.length; index++)
			bytesResult[index] = ((byte) Integer.parseInt(hexStr.substring(index << 1, (index << 1) + 2), 16));
		return bytesResult;
	}
}
